package xing2387.filtervideoplayer.filter;

import android.opengl.GLES20;
import android.util.Log;

import xing2387.filtervideoplayer.player.gl.FramebufferObject;

import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;

/**
 * https://www.shadertoy.com/view/Xltfzj
 * https://www.shadertoy.com/view/wtlczS#
 */

public class GlBoxBlurFilter extends GlFilter {

    private InnerFilter filterX = new InnerFilter(FRAGMENT_SHADER_BLUR_X);
    private InnerFilter filterY = new InnerFilter(FRAGMENT_SHADER_BLUR_Y);
    private FramebufferObject framebufferObject = new FramebufferObject();

    public GlBoxBlurFilter() {

    }

    @Override
    public void setFrameSize(int width, int height) {
        super.setFrameSize(width, height);
        framebufferObject.setup((int) (width * 1f), (int) (height * 1f));
        filterX.setFrameSize(width, height);
        filterY.setFrameSize(width, height);
    }

    @Override
    public void setVideoSize(int width, int height) {
        filterX.setVideoSize(width, height);
        filterY.setVideoSize(width, height);
    }

    @Override
    public void setup() {
        filterX.setup();
        filterY.setup();
    }

    @Override
    public void release() {
        filterX.release();
        filterY.release();
    }

    @Override
    public void draw(int texName, FramebufferObject fbo) {
        framebufferObject.enable();
        filterY.draw(texName, fbo);
        fbo.enable();
        filterX.draw(framebufferObject.getTexName(), fbo);
//        filterY.draw(texName, fbo);
    }

    private static final String FRAGMENT_SHADER_BLUR_X =
            "precision highp float;\n" +

                    "varying highp vec2 vTextureCoord;\n" +

                    "uniform lowp sampler2D sTexture\n;" +
                    "uniform highp float offsetY;\n" +
                    "uniform highp float offsetX;\n" +
                    "uniform highp float blurX;\n" +
                    "uniform highp float blurY;\n" +

                    "uniform highp float videoW;\n" +
                    "uniform highp float videoH;\n" +
                    "const float R = 12.0;\n" +

                    "highp vec4 blur(sampler2D image, vec2 fragCoord, vec2 resolution) {\n" +
                    "    vec2 _uv = fragCoord / resolution.xy;\n" +
                    "    vec3 sum_c = texture2D(image, _uv).rgb;\n" +
                    "    vec2 offset = vec2(1.0 / resolution.x, 0.0);\n" +
                    "    for(float i = 1.5; i <= R; i+=2.0) {\n" +
                    "        sum_c += texture2D(image, _uv + offset * i).rgb * 2.0;\n" +
                    "        sum_c += texture2D(image, _uv - offset * i).rgb * 2.0;\n" +
                    "    }\n" +
                    "    return vec4(sum_c / (2.0 * R ), 1.0);\n" +
                    "}" +

                    "void main() {\n" +
                    "   highp vec2 uv = vTextureCoord; \n" +
                    "   if(uv.x < offsetX || uv.x > (1.0 - offsetX) || uv.y < offsetY || uv.y > (1.0 - offsetY)) {\n" +
                    "       highp float u = blurX + uv.x * (1.0 - blurX * 2.0);\n" +
                    "       highp float v = blurY + uv.y * (1.0 - blurY * 2.0);\n" +
                    "       highp float frameW = videoW / (1.0 - offsetX * 2.0);\n" +
                    "       highp float frameH = videoH / (1.0 - offsetY * 2.0);\n" +
//                    "       gl_FragColor = blur(sTexture, vec2(uv.x * frameW, uv.y * frameH), vec2(frameW, frameH)); \n" +
                    "       gl_FragColor = blur(sTexture, vec2(uv.x * videoW, uv.y * videoH), vec2(videoW, videoH)); \n" +
//                    "       gl_FragColor = blur(sTexture, vec2(u * videoW, v * videoH), vec2(videoW, videoH)); \n" +
                    "   } else {\n" +
                    "       highp vec4 toColor = texture2D(sTexture, uv);\n" +
                    "       gl_FragColor = toColor;\n" +
                    "   }\n" +
                    "} \n";

    private static final String FRAGMENT_SHADER_BLUR_Y =
            "precision highp float;\n" +

                    "varying highp vec2 vTextureCoord;\n" +

                    "uniform lowp sampler2D sTexture\n;" +
                    "uniform highp float offsetY;\n" +
                    "uniform highp float offsetX;\n" +
                    "uniform highp float blurX;\n" +
                    "uniform highp float blurY;\n" +

                    "uniform highp float videoW;\n" +
                    "uniform highp float videoH;\n" +
                    "const float R = 12.0;\n" +

                    "highp vec4 blur(sampler2D image, vec2 fragCoord, vec2 resolution) {\n" +
                    "    vec2 _uv = fragCoord / resolution.xy;\n" +
                    "    vec3 sum_c = texture2D(image, _uv).rgb;\n" +
                    "    vec2 offset = vec2(0.0,  1.0 / resolution.y);\n" +
                    "    for(float i = 1.5; i <= R; i+=2.0){\n" +
                    "        sum_c += texture2D(image, _uv + offset * i).rgb * 2.0;\n" +
                    "        sum_c += texture2D(image, _uv - offset * i).rgb * 2.0;\n" +
                    "    }\n" +
                    "    return vec4(sum_c / (2.0 * R ), 1.0);\n" +
                    "}" +

                    "void main() {\n" +
                    "   highp vec2 uv = vTextureCoord; \n" +
                    "   if(uv.x < offsetX || uv.x > (1.0 - offsetX) || uv.y < offsetY || uv.y > (1.0 - offsetY)) {\n" +
                    "       highp float u = blurX + uv.x * (1.0 - blurX * 2.0);\n" +
                    "       highp float v = blurY + uv.y * (1.0 - blurY * 2.0);\n" +
                    "       gl_FragColor = blur(sTexture, vec2(u * videoW, v * videoH), vec2(videoW, videoH)); \n" +
                    "   } else {\n" +
                    "       highp float u = (uv.x - offsetX) / (1.0 - offsetX * 2.0);\n" +
                    "       highp float v = (uv.y - offsetY) / (1.0 - offsetY * 2.0);\n" +
                    "       highp vec2 toTexture = vec2(u, v);\n" +
                    "       highp vec4 toColor = texture2D(sTexture, toTexture);\n" +
                    "       gl_FragColor = toColor;\n" +
                    "   }\n" +
                    "} \n";


    private static class InnerFilter extends GlFilter {

        private static final String VERTEX_SHADER =
                "attribute vec4 aPosition;" +
                        "attribute vec4 aTextureCoord;\n" +
                        "varying highp vec2 vTextureCoord;\n" +
                        "void main() {" +
                        "gl_Position = aPosition;" +
                        "vTextureCoord = vec2(aTextureCoord.x, 1.0 - aTextureCoord.y);" +
                        "}";
        private float offsetX = 0;
        private float offsetY = 0;

        private float blurOffsetX = 0;
        private float blurOffsetY = 0;

        private float scaledVideoW;
        private float scaledVideoH;

        public InnerFilter(String fragmentShader) {
            super(VERTEX_SHADER, fragmentShader);
        }

        //mWidth surface宽度
        public void setTextureOffset(float x, float y) {
            this.offsetX = x;
            this.offsetY = y;
        }

        public void setBlurOffset(float x, float y) {
            this.blurOffsetX = x;
            this.blurOffsetY = y;
        }

        @Override
        public void setFrameSize(int width, int height) {
            super.setFrameSize(width, height);
            adjustOffset();
        }

        @Override
        public void setVideoSize(int width, int height) {
            super.setVideoSize(width, height);
            adjustOffset();
        }

        private static final String TAG = "GlGaussianBlurFilter";

        public void adjustOffset() {
            if (mWidth == 0 || mHeight == 0 || videoH == 0 || videoW == 0) {
                return;
            }
            try {
                float offsetX = 0;
                float offsetY = 0;
                float blurX = 0;
                float blurY = 0;
                float scaledH = mHeight;
                float scaledW = mWidth;
                if (mWidth * videoH > videoW * mHeight) {       //surface宽高比 比 视频的大
                    offsetX = ((mWidth - (mHeight * videoW / (float) videoH)) / 2) / mWidth;
                    scaledH = (mWidth * videoH / (float) videoW);
                    blurY = (scaledH - mHeight) / (2 * scaledH);
                } else {
                    offsetY = ((mHeight - (mWidth * videoH / (float) videoW)) / 2) / mHeight;
                    scaledW = (mHeight * videoW / (float) videoH);
                    blurX = (scaledW - mWidth) / (2 * scaledW);
                }
                Log.d(TAG, "adjustOffset mWidth " + mWidth + " mHeight " +
                        mHeight + " videoW " + videoW + " videoH " + videoH +
                        " offsetX " + offsetX + " offsetY " + offsetY);
                scaledVideoH = scaledH;
                scaledVideoW = scaledW;
                setTextureOffset(offsetX, offsetY);
                setBlurOffset(blurX, blurY);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDraw() {
            GLES20.glUniform1f(getHandle("offsetX"), offsetX);
            GLES20.glUniform1f(getHandle("offsetY"), offsetY);
            GLES20.glUniform1f(getHandle("videoW"), mWidth / 3);
            GLES20.glUniform1f(getHandle("videoH"), mHeight / 3);
            GLES20.glUniform1f(getHandle("blurX"), blurOffsetX);
            GLES20.glUniform1f(getHandle("blurY"), blurOffsetY);
        }
    }
}
